﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.MediaCenter.Store;
using Microsoft.MediaCenter.Guide;
using Microsoft.MediaCenter.Satellites;
using Microsoft.MediaCenter.TV.Tuning;
using Microsoft.MediaCenter.Pvr;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Windows.Controls;
using System.Windows;
using Microsoft.MediaCenter.UI;

namespace DVBS_HD_Wizard
{
    class SSPHD3
    {
        internal const int SID = 3822;
        internal const int Frequency = 12363;
        internal const int Sat = 282;
        internal const int TID = 2034;
        internal const int Rate = 29500;
        internal const int ONID = 2;
        internal const String Name = "Sky Sports HD3";
        internal const int Number = 5022;

        Dictionary<long, Tuner> tuners = new Dictionary<long, Tuner>();
        HashSet<Channel> channels = new HashSet<Channel>();
        DvbsSatellite satellite = null;
        DvbsTransponder transponder = null;
        DvbsService dvbsService = null;
        Service service = null;

        internal static string MatchName
        {
            get
            {
                return string.Format("DVBS:{0}:{1}:{2}:{3}:{4}", Sat, Frequency, ONID, TID, SID);
            }
        }

        internal void CreateSSPHD3(EditableText log, ObjectStore store)
        {
            if (dvbsService != null)
            {
                log.Value += "Service already exists - cannot proceed\r\n";
                return;
            }

            if (channels.Count > 0)
            {
                log.Value += "Channels already exist - cannot proceed\r\n";
                return;
            }

            // Create service
            if (service == null)
            {
                service = new Service();
                service.Name = Name;
                service.CallSign = Name;
                service.ServiceType = ServiceType.TV;
                service.IsHDCapable = true;
                store.Add(service);
            }

            // Create DvbsService
            if (dvbsService == null)
            {
                dvbsService = new DvbsService(string.Empty, 0, 0, false);
                transponder.Services.Add(dvbsService);
                dvbsService.Name = Name;
                dvbsService.ServiceId = SID;
                dvbsService.ServiceType = new DvbsServiceType?(DvbsServiceType.TV);
                dvbsService.IsEncrypted = false;
                dvbsService.Update();
            }

            foreach (Tuner tuner in tuners.Values)
            {
                // Create Channel
                Channel channel = new Channel();
                store.Add(channel);
                channel.CallSign = Name;
                channel.MatchName = MatchName;
                channel.ChannelType = ChannelType.CalculatedScanned;
                channel.Visibility = ChannelVisibility.Available;
                channel.Number = Number;
                channel.SubNumber = 0;
                channel.OriginalNumber = Number;
                channel.OriginalSubNumber = 0;
                channel.Service = service;
                channel.TuningInfos.Clear();

                foreach (long deviceId in tuner.devices.Keys)
                {
                    Device device = tuner.devices[deviceId];
                    LowNoiseBlock lnb = tuner.lnbs[deviceId];
                    DVBTuneRequest tuneRequest = dvbsService.GetTuneRequest((DVBSTuningSpace)device.DeviceType.TuningSpace);
                    tuneRequest.Locator = transponder.Modulation.GetLocator(lnb);
                    DvbTuningInfo info = new DvbTuningInfo(device, SerializationFormat.BinaryFormatter, SerializeTuneRequest(tuneRequest), ONID, tuneRequest);
                    info.IsEncrypted = false;
                    info.IsSuggestedBlocked = false;
                    info.IsSubscribed = true;

                    channel.TuningInfos.Add(info);
                }
                channel.Update();
                tuner.lineup.AddChannel(channel);
                tuner.lineup.NotifyChannelAdded(channel);
                tuner.lineup.RaiseLineupUpdatedEvent();
            }

            log.Value += String.Format("* Added: {1} = {0}\r\n", dvbsService.Name, dvbsService.ServiceId);
        }

        public enum CheckResult
        {
            Failed,
            NotNeeded,
            Needed,
            Updated
        }

        internal CheckResult PreChecks(EditableText log, ObjectStore store)
        {
            log.Value += "Performing Pre-Checks\r\n";

            log.Value += "Finding enabled devices\r\n";
            foreach (DeviceGroup group in new DeviceGroups(store))
            {
                if (group.IsEnabled && group.ContainsTunerType(TunerType.DvbS))
                {
                    // Enabled DVB-S devices

                    log.Value += "Looking for devices\r\n";
                    foreach (Device dev in group.Devices)
                    {
                        if (dev.DeviceType.TuningSpaceName == "DVB-S")
                        {
                            log.Value += String.Format("* {0}\r\n", dev.Name);
                            if (dev.LowNoiseBlocks == null)
                            {
                                log.Value += String.Format(" # No LNBs - Skipping\r\n");
                                continue;
                            }

                            foreach (LowNoiseBlock lnb in dev.LowNoiseBlocks)
                            {
                                if (lnb.Satellite == null)
                                {
                                    log.Value += String.Format("  # LNB doesn't have a satellite - Skipping\r\n");
                                    continue;
                                }

                                if (lnb.Satellite.PositionEast == Sat)
                                {
                                    log.Value += String.Format(" * {0}\r\n", lnb.Satellite.Name);
                                    if (dev.ScannedLineup != null)
                                    {
                                        if (!tuners.ContainsKey(dev.ScannedLineup.Id))
                                        {
                                            tuners[dev.ScannedLineup.Id] = new Tuner();
                                        }
                                        tuners[dev.ScannedLineup.Id].lineup = dev.ScannedLineup;
                                        tuners[dev.ScannedLineup.Id].lnbs.Add(dev.Id, lnb);
                                        tuners[dev.ScannedLineup.Id].devices.Add(dev.Id, dev);

                                        break;
                                    }
                                }
                                else
                                {
                                    log.Value += String.Format(" - {0} Ignored\r\n", lnb.Satellite.Name);
                                }
                            }
                        }
                    }
                }
            }

            if (tuners.Count == 0)
            {
                log.Value += String.Format(" # Unable to determine lineup\r\n");
                return CheckResult.Failed;
            }

            log.Value += "Looking for satellite\r\n";
            // Find Asta 2D
            foreach (DvbsSatellite sat in new DvbsSatellites(store))
            {
                if (sat.PositionEast == SSPHD3.Sat)
                {
                    satellite = sat;
                    log.Value += String.Format("* {0}\r\n", satellite.Name);
                    break;
                }
            }

            if (satellite == null)
            {
                log.Value += "Failed to find satellite\r\n";
                return CheckResult.Failed;
            }

            log.Value += "Looking for transponder\r\n";
            foreach (DvbsTransponder trans in satellite.Transponders)
            {
                if (trans.Modulation.CarrierFrequency == SSPHD3.Frequency)
                {
                    if (trans.Modulation.SymbolRate == Rate)
                    {
                        transponder = trans;
                        log.Value += String.Format("* {0} (NID:{1} TID:{2} ONID:{3})\r\n",
                            transponder.Modulation.ToString(),
                            transponder.NetworkId,
                            transponder.TransportStreamId,
                            transponder.OriginalNetworkId);

                        break;
                    }
                }
            }

            if (transponder==null)
            {
                // Fetch Astra 2D by UID
                DvbsSatellite sat = new DvbsSatellites(store).WithUId("!DvbsDataSet!DvbsSatellite[282]");

                // Create a new modulation (frequency, polarization, symbol rate)
                DvbsModulation modulation = new DvbsModulation(12363, DvbsPolarization.LinearVertical, 29500);

                // Create the transponder (modulation, TID, NID, 0)
                 transponder = new DvbsTransponder(modulation, 2034, new ushort?(2), new ushort?(0));

                // Finally add transponder to satellite's transponder list
                sat.Transponders.Add(transponder);
                sat.Update();
            }

            log.Value += "Looking for channels\r\n";
            foreach (Tuner tuner in tuners.Values)
            {
                foreach (Channel chan in tuner.lineup.UncachedChannels)
                {
                    foreach (TuningInfo info2 in chan.TuningInfos)
                    {
                        if (info2 is DvbTuningInfo)
                        {
                            DvbTuningInfo info = (DvbTuningInfo)info2;
                            if (tuner.devices.Keys.Contains(info.Device.Id))
                            {
                                if (info.Frequency == Frequency * 1000 && info.Sid == SID)
                                {
                                    if (!chan.Service.IsHDCapable)
                                    {
                                        log.Value += String.Format(" * {0} not marked as HD\r\n", chan.CallSign);
                                       
                                            chan.Service.IsHDCapable = true;
                                            chan.Service.Update();
                                            tuner.lineup.RaiseLineupUpdatedEvent();
                                            log.Value += String.Format(" * {0} updated. No further action needed.\r\n", chan.CallSign);
                                            return CheckResult.Updated;
                                        

                                        
                                    }
                                    log.Value += String.Format(" * {0} found and HD\r\n", chan.CallSign);
                                    log.Value += String.Format("System does not need further updating\r\n", chan.CallSign);

                                    channels.Add(chan);
                                    return CheckResult.NotNeeded;
                                }
                            }
                        }
                    }
                }
            }

            log.Value += "Looking for dvbsservice\r\n";
            foreach (DvbsService serv in transponder.Services)
            {
                //logListBox.Items.Add(String.Format("* {0} ({1})", serv.Name, serv.ServiceId));
                if (serv.ServiceId == SSPHD3.SID)
                {
                    dvbsService = serv;
                    log.Value += String.Format("* Already Exists: {1} = {0}\r\n", dvbsService.Name, dvbsService.ServiceId);
                    return CheckResult.NotNeeded;
                }
            }

            log.Value += "Pre-Checks Passed\r\n";
            return CheckResult.Needed;
        }

        private static byte[] SerializeTuneRequest(TuneRequest tuneRequest)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                new BinaryFormatter().Serialize(stream, tuneRequest);
                return stream.ToArray();
            }
        }
    }
}

